package com.overkill.game;

import java.util.ArrayList;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.KeyEvent;

/**
 * Provides the basic functions for a game engine
 * This class should be extended by your own game logic
 * @author ov3rk1ll
 *
 */
public class GameEngine {

	protected RenderView renderView;
	protected Context context;
	protected static Rect stage = new Rect();
	protected GameOptions options = new GameOptions();
	protected GameOverListener gameOverListener;
	
	protected int targetFps = 30;
	
	/**
	 * A value representing the pixel density change from mdip
	 * @see http://developer.android.com/reference/android/util/DisplayMetrics.html#density
	 */
	public static float SCALE = 1;
	
	public static int DEVICE_DENSITY = 1;
	
	/**
	 * Return the scaled value of the given value
	 * @param value Value to scale
	 * @return Scaled value
	 */
	public static float dip(float value){ return value * SCALE; }
	
	public static Rect getStage(){ return stage; }
	
	public boolean gameRunning = false;
	public boolean gamePaused = false;
	
	ArrayList<Renderable> renderQueue = new ArrayList<Renderable>(); 
	
	/**
	 * Creates a instance with the given parameter and defaults the target FPS to 30
	 * @param options {@link GameOptions} to pass to your logic
	 * @param gameOverListener A {@link GameOverListener} to be called by the gameOver method
	 */
	public GameEngine(GameOptions options, GameOverListener gameOverListener){
		this(options, gameOverListener, 30);
	}
	
	/**
	 * Creates a instance with the given parameter
	 * @param options {@link GameOptions} to pass to your logic
	 * @param gameOverListener A {@link GameOverListener} to be called by the gameOver method
	 * @param targetFps The frames per second you want to render
	 */
	public GameEngine(GameOptions options, GameOverListener gameOverListener, int targetFps){		
		this.options = options;
		this.gameOverListener = gameOverListener;
		this.targetFps = targetFps;
	}
			
	/**
	 * This is called after the constructor and before the first update or draw cycle<br />
	 * <b>This function should be overwritten and used for defaulting any game values</b><br />
	 * <i>Initialize is also called by any restart method</i>
	 * @param stage This represents the size of the {@link RenderView}
	 */
	public void inizialize(Rect stage){
		GameEngine.stage = stage;
	}
		
	public void restart(){
		renderView.initialize(this.getTargetFps());
		renderView.start();
	}
	
	/**
	 * Updates all {@link Renderable}s in the render queue<br />
	 * <b>Should be overwritten by your own game logic</b>
	 * @param gameTime A time value in ms that will get higher with every call
	 * @param deltaTime The time that passed between two update calls in ms
	 */
	public void update(long gameTime, int deltaTime){
		ArrayList<Renderable> removeFromQueue = new ArrayList<Renderable>();
		for (Renderable r : renderQueue) {
			if(r.isVisible() == false && r.isPresist() == false){
				removeFromQueue.add(r);
			}else{
				r.update(gameTime);
			}
		}		
	}
	
	/**
	 * Requests the renderView to draw one single frame<br />
	 * The update function will <b>NOT</b> be called before the draw 
	 */
	public void requestDraw(){
		this.renderView.drawFrame();
	}
	
	/**
	 * Draws all {@link Renderable} in the render queue with the last item being on top.<br />
	 * <b>Should be overwritten to draw item not in the queue</b>
	 * @param canvas The {@link Canvas to draw on}
	 */
	public void draw(Canvas canvas){
		for (Renderable r : renderQueue) {
			r.draw(canvas);
		}		
	}
	
	/**
	 * Calls the {@link GameOverListener} with any empty {@link GameOptions} object 
	 */
	public void gameOver(){
		this.gameOver(new GameOptions());
	}
	
	/**
	 * Calls the {@link GameOverListener} with the given {@link GameOptions} object .
	 * @param options The {@link GameOptions} object to pass to the {@link GameOverListener}
	 */
	public void gameOver(GameOptions options){
		this.gameRunning = false;
		this.renderView.stopRender();
		this.renderView.destroy();
		this.gameOverListener.gameOver(options);

	}
	
	/**
	 * Adds a {@link Renderable} to the top of the render queue.
	 * The new item will be updated last and drawn in the foreground.
	 * @param renderable The {@link Renderable} to add
	 */
	public void addToQueue(Renderable renderable){
		renderQueue.add(renderable);
	}
	
	/**
	 * Removed an {@link Renderable} from the render queue
	 * @param renderable The {@link Renderable} to remove
	 */
	public void removeFromQueue(Renderable renderable){
		renderQueue.remove(renderable);
	}
	
	/**
	 * Returns the size of the render queue
	 * @return The size of the render queue
	 */
	public int getQueueSize() {
		return renderQueue.size();
	}
		
	/**
	 * Returns the current {@link GameOptions}
	 * @return The current {@link GameOptions}
	 */
	public GameOptions getOptions(){
		return options;
	}

	/**
	 * Returns the current {@link Context}
	 * @return The current {@link Context}
	 */	
	public Context getContext() {
		return context;
	}

	/**
	 * Sets the {@link Context}<br />
	 * This is called automatically by the {@link RenderView#setEngine(GameEngine engine)} function
	 * @param context The {@link Context} instance of the {@link RenderView}
	 */
	public void setContext(Context context) {
		this.context = context;
	}

	public RenderView getRenderView() {
		return renderView;
	}

	public void setRenderView(RenderView renderView) {
		this.renderView = renderView;
	}
	
	public void pauseGame() {
		gamePaused = true;
		renderView.pauseRender(true);
	}

	public void resumeGame() {
		gamePaused = false;
		renderView.resumeRender();
	}

	public boolean isGameRunning() {
		return gameRunning;
	}

	public void setGameRunning(boolean gameRunning) {
		this.gameRunning = gameRunning;
	}

	public boolean isGamePaused() {
		return gamePaused;
	}

	public void setGamePaused(boolean gamePaused) {
		this.gamePaused = gamePaused;
	}
	
	public void setGameOverListener(GameOverListener gameOverListener) {
		this.gameOverListener = gameOverListener;
	}

	public int getTargetFps() {
		return targetFps;
	}

	public void setTargetFps(int targetFps) {
		this.targetFps = targetFps;
	}

	public GameOverListener getGameOverListener() {
		return gameOverListener;
	}
	
	/**
	 * Return a localized string from the application's package's default string table.
	 * @param resId Resource id for the string  
	 */
	public String getString(int resId) {
		return getContext().getString(resId);
	}
	
	/**
	 * Return a localized formatted string from the application's package's default string table, 
	 * substituting the format arguments as defined in Formatter and format(String, Object...).
	 * @param resId Resource id for the format string 
	 * @param formatArgs The format arguments that will be used for substitution.  
	 * @return
	 */
	public String getString(int resId, Object... formatArgs) {
		return getContext().getString(resId, formatArgs);
	}
	
	public void handleTouchEvent(int action, float x, float y){		
	}
	
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		return false;
	}
	
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		return false;		
	}
}

